package cn.coisini.utils;

import cn.coisini.config.MinIoConfig;
import io.minio.*;
import io.minio.http.Method;
import jakarta.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor;
import lombok.extern.log4j.Log4j2;
import org.apache.tomcat.util.http.fileupload.IOUtils;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URLEncoder;
import java.util.Optional;
import java.util.concurrent.TimeUnit;

/**
 * @author xiangshaw
 * description: Minio工具类
 */
@Log4j2
@Component
@RequiredArgsConstructor
public class MinioUtil {

    private final MinioClient minioClient;

    private final MinIoConfig minIoConfig;

    /**
     * 判断bucket是否存在，不存在则创建
     */
    public boolean ensureBucketExists(String bucketName) {
        try {
            if (!minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build())) {
                minioClient.makeBucket(MakeBucketArgs.builder().bucket(bucketName).build());
            }
            return true;
        } catch (Exception e) {
            log.error("Failed to ensure Minio bucket existence: bucketName={}, error={}", bucketName, e.getMessage(), e);
            return false;
        }
    }

    /**
     * 删除bucket
     */
    public boolean removeBucket(String bucketName) {
        try {
            minioClient.removeBucket(RemoveBucketArgs.builder().bucket(bucketName).build());
            return true;
        } catch (Exception e) {
            log.error("Failed to remove Minio bucket: bucketName={}, error={}", bucketName, e.getMessage(), e);
            return false;
        }
    }

    /**
     * 上传文件
     *
     * @param file     文件
     * @param fileName 文件名称
     */
    public void upload(MultipartFile file, String fileName) {
        try (InputStream inputStream = file.getInputStream()) {
            minioClient.putObject(PutObjectArgs.builder()
                    .bucket(minIoConfig.getBucketName())
                    .object(fileName)
                    .stream(inputStream, file.getSize(), -1)
                    .contentType(file.getContentType())
                    .build());
            log.info("File uploaded to Minio successfully: fileName={}, bucketName={}, fileSize={}",
                    fileName, minIoConfig.getBucketName(), file.getSize());
        } catch (Exception e) {
            log.error("Failed to upload file to Minio: fileName={}, error={}", fileName, e.getMessage(), e);
        }
    }

    /**
     * 获取文件访问地址（有过期时间）
     *
     * @param fileName 文件名称
     * @param time     时间
     * @param timeUnit 时间单位
     */
    public String getExpireFileUrl(String fileName, int time, TimeUnit timeUnit) {
        try {
            return minioClient.getPresignedObjectUrl(GetPresignedObjectUrlArgs.builder()
                    .method(Method.GET)
                    .bucket(minIoConfig.getBucketName())
                    .object(fileName)
                    .expiry(time, timeUnit).build());
        } catch (Exception e) {
            log.error("Failed to get expire file URL from Minio: fileName={}, error={}", fileName, e.getMessage(), e);
            throw new RuntimeException("URL生成失败");
        }
    }

    /**
     * 获取文件访问地址
     *
     * @param fileName 文件名称
     */
    public Optional<String> getFileUrl(String fileName) {
        try {
            String url = minioClient.getPresignedObjectUrl(GetPresignedObjectUrlArgs.builder()
                    .method(Method.GET)
                    .bucket(minIoConfig.getBucketName())
                    .object(fileName)
                    .build());
            log.info("Successfully generated presigned URL: fileName={}, bucketName={}",
                    fileName, minIoConfig.getBucketName());
            return Optional.of(url);
        } catch (Exception e) {
            log.error("Failed to get file URL from Minio: fileName={}, error={}", fileName, e.getMessage(), e);
            return Optional.empty();
        }
    }

    /**
     * 下载文件
     *
     * @param fileName 文件名称
     */
    public void download(HttpServletResponse response, String fileName) {
        try {
            // 获取对象信息
            StatObjectResponse stat = minioClient.statObject(
                    StatObjectArgs.builder()
                            .bucket(minIoConfig.getBucketName())
                            .object(fileName)
                            .build()
            );
            response.setContentType(stat.contentType());
            response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(fileName, "UTF-8"));

            // 使用 try-with-resources 自动关闭 InputStream
            try (InputStream in = minioClient.getObject(
                    GetObjectArgs.builder()
                            .bucket(minIoConfig.getBucketName())
                            .object(fileName)
                            .build());
                 OutputStream out = response.getOutputStream()) {
                // 文件下载
                IOUtils.copy(in, out);
                // 确保所有数据都写入输出流
                out.flush();
            } catch (IOException e) {
                log.error("Failed to download file: fileName={}, error={}", fileName, e.getMessage(), e);
            }
        } catch (Exception e) {
            log.error("Failed to retrieve object information from Minio: fileName={}, error={}", fileName, e.getMessage(), e);
        }
    }

    /**
     * 删除文件
     *
     * @param fileName 文件名称
     */
    public boolean delete(String fileName) {
        try {
            minioClient.removeObject(RemoveObjectArgs.builder()
                    .bucket(minIoConfig.getBucketName())
                    .object(fileName)
                    .build());
            log.info("Successfully deleted file: {}", fileName);
            return true;
        } catch (Exception e) {
            log.error("Failed to delete file from Minio: fileName={}, error={}", fileName, e.getMessage(), e);
            return false;
        }
    }
}
